import PySpin
import sys
'''
这个程序主要用于展示如何使用节点回调。因为回调是和事件类似，所以可以作为事件的基础.
这个程序主要在1.创建回调（HeightNodeCallback，GainNodeCallback，这两个是类）；2注册回调configure_callbacks，回调需要一个带有回调函数签名的回调类，
允许注册并访问一个节点。事件也遵循同样的模式。
3.使用回调change_height_and_gain；4.注销回调reset_callbacks
'''

class HeightNodeCallback(PySpin.NodeCallback):
    def __init__(self):
        super().__init__()

    def CallbackFunction(self, node):
        node_height = PySpin.CIntegerPtr(node)
        print('高度回调信息，高度倍改变为了%f' %node_height.GetValue())

class GainNodeCallback(PySpin.NodeCallback):
    def __init__(self):
        super().__init__()
    def CallbackFunction(self, node):
        node_gain = PySpin.CFloatPtr(node)
        print('增益回调信息，增益改变为了%f' % node_gain.GetValue())

def configure_callbacks(nodemap):
    '''
    这个函数通过禁用自动增益，创建回调，注册他们到具体的节点
    :param nodemap:
    :return:
    '''
    print('***** 设置回调 *****')
    try:
        result = True
        #关闭自动增益
        #自动增益会阻止手动设置增益，在这个例子中需要关闭它
        node_gain_auto = PySpin.CEnumerationPtr(nodemap.GetNode('GainAuto'))
        if not PySpin.IsAvailable(node_gain_auto) or not PySpin.IsWritable(node_gain_auto):
            print('无法关闭自动增益')
            return False

        node_gain_auto_off = PySpin.CEnumEntryPtr(node_gain_auto.GetEntryByName('Off'))
        if not PySpin.IsAvailable(node_gain_auto_off) or not PySpin.IsReadable(node_gain_auto_off):
            print('无法关闭自动增益')
            return False
        node_gain_auto.SetIntValue(node_gain_auto_off.GetValue())
        print('已经禁用自动增益')

        #注册回调的高度节点
        #注意：回调需要注册节点,之后要记得注销节点
        node_height = PySpin.CIntegerPtr(nodemap.GetNode('Height'))
        if not PySpin.IsAvailable(node_height) or not PySpin.IsWritable(node_height):
            print('无法注册高度节点')
            return False

        callback_height = HeightNodeCallback()
        PySpin.RegisterNodeCallback(node_height.GetNode(), callback_height) #注册node_height节点的回调签名

        print('高度回馈已经注册了')

        # 注册回调的增益节点
        # 注意：回调需要注册节点,之后要记得注销节点
        node_gain = PySpin.CFloatPtr(nodemap.GetNode('Gain'))
        if not PySpin.IsAvailable(node_gain) or not PySpin.IsWritable(node_gain):
            print('无法注册高度节点')
            return False

        callback_gain = GainNodeCallback()
        PySpin.RegisterNodeCallback(node_gain.GetNode(), callback_gain)  #注册node_gain节点的回调签名

        print('高度回馈已经注册了')
    except PySpin.SpinnakerException as ex:
        print('Error:%s' %ex)
        return False

    return result,callback_height,callback_gain


def change_height_and_gain(nodemap):
    '''
    这个函数用于触发节点图的回调，首先改变高，之后改变增益
    :param nodemap:
    :return:
    '''

    print('***** 改变高度和增益 ******')
    try:
        result = True
        #改变高度，并且触发高度反馈
        node_height = PySpin.CIntegerPtr(nodemap.GetNode('Height'))
        if not PySpin.IsAvailable(node_height) or not PySpin.IsWritable(node_height) or node_height.GetInc()==0\
            or node_height.GetMax()==0:
            print('不能检索高度')
            return False
        height_to_set = node_height.GetMax()
        print('函数通知信息：高度被改变为了 %i' %height_to_set)
        node_height.SetValue(height_to_set)

        #改变增益，并且触发增益反馈
        node_gain = PySpin.CFloatPtr(nodemap.GetNode('Gain'))
        if not PySpin.IsAvailable(node_gain) or not PySpin.IsWritable(node_gain) or node_gain.GetMax() == 0:
            print('不能检索增益')
            return False
        gain_to_set = node_gain.GetMax()/2.0
        print('函数通知信息：增益被改变为了 %f' %gain_to_set)
        node_gain.SetValue(gain_to_set)
    except PySpin.SpinnakerException as ex:
        print('Error: %s' %ex)
        return False
    return result

def reset_callbacks(nodemap, callback_height, callback_gain):
    '''
    这个函数用于清除这个实例，通过注销callback和重新打开自动增益
    :param nodemap:
    :param callback_height:
    :param callback_gain:
    :return:
    '''

    try:
        result = True
        #注销回调
        PySpin.DeregisterNodeCallback(callback_height)
        PySpin.DeregisterNodeCallback(callback_gain)
        print('回调已经被注销了')

        #重新开启自动增益
        node_gain_auto = PySpin.CEnumerationPtr(nodemap.GetNode('GainAuto'))
        if not PySpin.IsAvailable(node_gain_auto) or not PySpin.IsWritable(node_gain_auto):
            print('无法开启自动增益')
            return False
        #这里的获取有些许特殊
        node_gain_auto_continuous = PySpin.CEnumEntryPtr(node_gain_auto.GetEntryByName('Continuous'))
        if not PySpin.IsAvailable(node_gain_auto_continuous) or not PySpin.IsReadable(node_gain_auto_continuous):
            print('无法开启自动增益')
            return False
        node_gain_auto.SetIntValue(node_gain_auto_continuous.GetValue())
        print('重启了自动增益')

    except PySpin.SpinnakerException as ex:
        print('Error:%s' %ex)
        result = False

    return result



def run_single_camera(cam):
    try:
        result = True
        nodemap_tldevice = cam.GetTLDeviceNodeMap()
        cam.Init()
        nodemap = cam.GetNodeMap()
        #设置回调
        err, callback_height,callback_gain = configure_callbacks(nodemap)
        if not err:
            return err

        #通过改变高度和增益来触发回调
        result &= change_height_and_gain(nodemap)

        #重置回调
        result &= reset_callbacks(nodemap, callback_height, callback_gain)

        cam.DeInit()
    except PySpin.SpinnakerException as ex:
        print('Error:%s' %ex)
        return False

    return result

def main():
    result = True
    system = PySpin.System.GetInstance()
    cam_list = system.GetCameras()
    num_cams = cam_list.GetSize()
    print('%d台相机' %num_cams)

    if num_cams == 0:
        cam_list.Clear()
        system.ReleaseInstance()
        print('没有检测到相机')
        input('按enter退出')
        return False

    for i, cam in enumerate(cam_list):
        result = run_single_camera(cam)

    del cam
    cam_list.Clear()
    system.ReleaseInstance()
    return result

if __name__ == '__main__':
    if main():
        sys.exit(0)
    else:
        sys.exit(1)